import {h} from 'vue'
import screenfull from 'screenfull'
import throttle from 'lodash/throttle'
import html2canvas from 'html2canvas'
import {downloadByA} from './file'
import {toString} from './type'
import cloneDeep from 'lodash/cloneDeep'
import {CreateComponentType, CreateComponentGroupType} from '@/packages/index.d'
import {RequestHttpIntervalEnum, RequestParamsObjType} from '@/enums/httpEnum'
import {WinKeyboard} from '@/enums/editPageEnum'
import {excludeParseEventKeyList, excludeParseEventValueList} from '@/enums/eventEnum'

/**
 * * 判断是否是开发环境
 * @return { Boolean }
 */
export const isDev = () => {
    return import.meta.env.DEV
}

/**
 * * 生成一个不重复的ID
 * @param { Number } randomLength
 */
export const getUUID = (randomLength = 10) => {
    return Number(Math.random().toString().substring(2, randomLength) + Date.now()).toString(36)
}

interface TreeNode {
    id: string;
    name: string;
    parentId?: string | null;
    children?: TreeNode[];
}

/**
 * @description 父子关系的数组转换成树形结构数据
 * @param data
 * @returns {*}
 */
export const translateDataToTree = (data: TreeNode[]): TreeNode[] => {
    // 过滤出顶级节点（没有parentId或parentId为undefined/null的节点）
    const parent: TreeNode[] = data.filter(
        (value) => value.parentId === undefined || value.parentId === null
    );

    // 过滤出所有有parentId的节点
    const children: TreeNode[] = data.filter(
        (value) => value.parentId !== undefined && value.parentId !== null
    );

    const translator = (parents: TreeNode[], childNodes: TreeNode[]) => {
        parents.forEach((parent) => {
            childNodes.forEach((current, index) => {
                if (current.parentId === parent.id) {
                    const temp = [...childNodes]; // 使用解构来复制数组，避免直接修改原数组
                    temp.splice(index, 1);
                    translator([current], temp);

                    // 如果parent已经有children属性，则添加current到children数组中
                    // 否则，初始化children属性为包含current的数组
                    if (!parent.children) {
                        parent.children = [];
                    }
                    parent.children.push(current);
                }
            });
        });
    };

    translator(parent, children);
    return parent;
};

/**
 * @description 树形结构数据转换成父子关系的数组
 * @param data - 树形结构数据的数组
 * @returns 转换后的父子关系数组
 */
export const translateTreeToData = (data: TreeNode[]): { id: string; name: string; parentId?: string | null }[] => {
    const result: { id: string; name: string; parentId?: string | null }[] = [];

    const loop = (node: TreeNode) => {
        result.push({
            id: node.id,
            name: node.name,
            parentId: node.parentId,
        });

        if (node.children) {
            node.children.forEach(child => loop(child));
        }
    };

    data.forEach(item => loop(item));

    return result;
};

/**
 * * render 语言
 *  @param lang 语言标识
 *  @param set 设置项
 *  @param tag 要渲染成的标签
 */
export const renderLang = (lang: string, set = {}, tag = 'span') => {
    return () => h(tag, set, {default: () => window['$t'](lang)})
}

/**
 * * 全屏操作函数
 * @param isFullscreen
 * @param isEnabled
 * @returns
 */
export const screenfullFn = (isFullscreen?: boolean, isEnabled?: boolean) => {
    // 是否是全屏
    if (isFullscreen) return screenfull.isFullscreen

    // 是否支持全屏
    if (isEnabled) return screenfull.isEnabled

    if (screenfull.isEnabled) {
        screenfull.toggle()
        return
    }
    // TODO lang
    window['$message'].warning('您的浏览器不支持全屏功能！')
}

/**
 * 修改元素位置
 * @param target 对象
 * @param x X轴
 * @param y Y轴
 */
export const setComponentPosition = (
    target: CreateComponentType | CreateComponentGroupType,
    x?: number,
    y?: number
) => {
    x && (target.attr.x = x)
    y && (target.attr.y = y)
}

/**
 * * 设置元素属性
 * @param HTMLElement 元素
 * @param key 键名
 * @param value 键值
 */
export const setDomAttribute = <K extends keyof CSSStyleDeclaration, V extends CSSStyleDeclaration[K]>(
    HTMLElement: HTMLElement,
    key: K,
    value: V
) => {
    if (HTMLElement) {
        HTMLElement.style[key] = value
    }
}

/**
 * * 判断是否是 mac
 * @returns boolean
 */
export const isMac = () => {
    return /macintosh|mac os x/i.test(navigator.userAgent)
}

/**
 * * file转url
 */
export const fileToUrl = (file: File): string => {
    const Url = URL || window.URL || window.webkitURL
    const ImageUrl = Url.createObjectURL(file)
    return ImageUrl
}

/**
 * * file转base64
 */
export const fileTobase64 = (file: File, callback: Function) => {
    const reader = new FileReader()
    reader.readAsDataURL(file)
    reader.onload = function (e: ProgressEvent<FileReader>) {
        if (e.target) {
            const base64 = e.target.result
            callback(base64)
        }
    }
}

/**
 * * 挂载监听
 */

export const addEventListener = <K extends keyof WindowEventMap>(
    target: HTMLElement | Document,
    type: K,
    listener: any,
    delay?: number,
    options?: boolean | AddEventListenerOptions | undefined
) => {
    if (!target) return
    target.addEventListener(
        type,
        throttle(listener, delay || 300, {
            leading: true,
            trailing: false
        }),
        options
    )
}

/**
 * * 卸载监听
 */

export const removeEventListener = <K extends keyof WindowEventMap>(
    target: HTMLElement | Document,
    type: K,
    listener: any
) => {
    if (!target) return
    target.removeEventListener(type, listener)
}

/**
 * * 截取画面为图片并下载
 * @param html 需要截取的 DOM
 */
export const canvasCut = (html: HTMLElement | null, callback?: Function) => {
    if (!html) {
        window['$message'].error('导出失败！')
        if (callback) callback()
        return
    }

    html2canvas(html, {
        backgroundColor: null,
        allowTaint: true,
        useCORS: true
    }).then((canvas: HTMLCanvasElement) => {
        window['$message'].success('导出成功！')
        downloadByA(canvas.toDataURL(), undefined, 'png')
        if (callback) callback()
    })
}

/**
 * * 函数过滤器
 * @param data 数据值
 * @param res 返回顶级对象
 * @param funcStr 函数字符串
 * @param isToString 是否转为字符串
 * @param errorCallBack 错误回调函数
 * @param successCallBack 成功回调函数
 * @returns
 */
export const newFunctionHandle = (
    data: any,
    res: any,
    funcStr?: string,
    isToString?: boolean,
    errorCallBack?: Function,
    successCallBack?: Function
) => {
    try {
        if (!funcStr) return data
        const fn = new Function('data', 'res', funcStr)
        const fnRes = fn(cloneDeep(data), cloneDeep(res))
        const resHandle = isToString ? toString(fnRes) : fnRes
        // 成功回调
        successCallBack && successCallBack(resHandle)
        return resHandle
    } catch (error) {
        // 失败回调
        errorCallBack && errorCallBack(error)
        return '函数执行错误'
    }
}

/**
 * * 处理请求事件单位
 * @param num 时间间隔
 * @param unit RequestHttpIntervalEnum
 * @return number 秒数
 */
export const intervalUnitHandle = (num: number, unit: RequestHttpIntervalEnum) => {
    switch (unit) {
        // 秒
        case RequestHttpIntervalEnum.SECOND:
            return num * 1000
        // 分
        case RequestHttpIntervalEnum.MINUTE:
            return num * 1000 * 60
        // 时
        case RequestHttpIntervalEnum.HOUR:
            return num * 1000 * 60 * 60
        // 天
        case RequestHttpIntervalEnum.DAY:
            return num * 1000 * 60 * 60 * 24
        default:
            return num * 1000
    }
}

/**
 * timeFormat 时间格式转换
 * @param time
 * @param cFormat "{y}-{m}-{d} {h}:{i}:{s} 周{a}"
 * @returns {string|null}
 */
export const timeFormat = function (time: Date | string | number, cFormat: string) {
    if (arguments.length === 0) {
        return null;
    }
    const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}";
    let date;
    if (typeof time === "object") {
        date = time;
    } else {
        if (typeof time === "string" && /^[0-9]+$/.test(time)) {
            time = parseInt(time);
        }
        if (typeof time === "number" && time.toString().length === 10) {
            time = time * 1000;
        }
        date = new Date(time);
    }
    const formatObj = {
        y: date.getFullYear(),
        m: date.getMonth() + 1,
        d: date.getDate(),
        h: date.getHours(),
        i: date.getMinutes(),
        s: date.getSeconds(),
        a: date.getDay(),
    };
    const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
        let value = formatObj[key];
        if (key === "a") {
            return ["日", "一", "二", "三", "四", "五", "六"][value];
        }
        if (result.length > 0 && value < 10) {
            value = "0" + value;
        }
        return value || 0;
    });
    return time_str;
}

export const getWeek = function getWeekOfYear(date: Date) {
    // 设定当年1月1日为起始点
    const startOfYear = new Date(date.getFullYear(), 0, 1)
    // 获取1月1日是周几（0代表周日，1代表周一，依此类推）
    const startDay = startOfYear.getDay()

    // 若1月1日非周一，则调整至最近的下一个周一作为起始周的第一天
    const adjustedStart =
        startDay <= 0
            ? startOfYear
            : new Date(
                startOfYear.getTime() + (7 - startDay + 1) * 24 * 60 * 60 * 1000
            ) // 加1是为了包含1月1日所在的那一天
    // 若1月1日是周日，并且我们视周日为一周的最后一天，则可能需要将起始日调整至12月31日所在的那一周的周一（视具体需求而定）
    // 此处我们默认周一为一周起点，故不进行此调整

    // 计算给定日期与调整后的起始日之间的天数差
    const diffInMilliseconds = date - adjustedStart
    const diffInDays = Math.floor(diffInMilliseconds / (24 * 60 * 60 * 1000))

    // 将天数转换为周数，并加1以包含起始周（若需要的话，可以根据实际需求决定是否加1）
    const weekNumber = Math.ceil(diffInDays / 7) + 2 // 此处加1是因为我们的计算从周一开始，并且想要包含1月1日所在的那一周（即使它只有部分天数）

    // 特殊情况处理：若给定日期在1月1日所在的周之前（即前一年的最后几天），则可能需要返回上一年的最后几周（根据实际需求决定）
    // 此处我们默认不处理这种情况，即只考虑当前年份内的周数

    // 另外，若你的业务逻辑中一周的起始日不是周一，而是周日或其他日期，你需要相应地调整上述代码

    return weekNumber
}

/**
 * * 对象转换 cookie 格式
 * @param obj
 * @returns string
 */
export const objToCookie = (obj: RequestParamsObjType) => {
    if (!obj) return ''

    let str = ''
    for (const key in obj) {
        str += key + '=' + obj[key] + ';'
    }
    return str.substring(0, str.length - 1)
}

/**
 * * 设置按下键盘按键的底部展示
 * @param keyCode
 * @returns
 */
export const setKeyboardDressShow = (keyCode?: number) => {
    const code = new Map([
        [17, WinKeyboard.CTRL],
        [32, WinKeyboard.SPACE]
    ])

    const dom = document.getElementById('keyboard-dress-show')
    if (!dom) return
    if (!keyCode) {
        window.onKeySpacePressHold?.(false)
        dom.innerText = ''
        return
    }
    if (keyCode && code.has(keyCode)) {
        if (keyCode == 32) window.onKeySpacePressHold?.(true)
        dom.innerText = `按下了「${code.get(keyCode)}」键`
    }
}

/**
 * * JSON序列化，支持函数和 undefined
 * @param data
 */
export const JSONStringify = <T>(data: T) => {
    return JSON.stringify(
        data,
        (key, val) => {
            // 处理函数丢失问题
            if (typeof val === 'function') {
                return `${val}`
            }
            // 处理 undefined 丢失问题
            if (typeof val === 'undefined') {
                return null
            }
            return val
        },
        2
    )
}

export const evalFn = (fn: string) => {
    const Fun = Function // 一个变量指向Function，防止前端编译工具报错
    return new Fun('return ' + fn)()
}


// 内置字符串函数键值列表
export const excludeParseEventValueList = [
    // 请求里的函数语句
    'javascript:'
]

/**
 * * JSON反序列化，支持函数和 undefined
 * @param data
 */
export const JSONParse = (data: string) => {
    return JSON.parse(data, (k, v) => {
        // 过滤函数字符串
        if (excludeParseEventKeyList.includes(k)) return v
        // 过滤函数值表达式
        if (typeof v === 'string') {
            const someValue = excludeParseEventValueList.some(excludeValue => v.indexOf(excludeValue) > -1)
            if (someValue) return v
        }
        // 还原函数值
        if (typeof v === 'string' && v.indexOf && (v.indexOf('function') > -1 || v.indexOf('=>') > -1)) {
            return evalFn(`(function(){return ${v}})()`)
        } else if (typeof v === 'string' && v.indexOf && v.indexOf('return ') > -1) {
            const baseLeftIndex = v.indexOf('(')
            if (baseLeftIndex > -1) {
                const newFn = `function ${v.substring(baseLeftIndex)}`
                return evalFn(`(function(){return ${newFn}})()`)
            }
        }
        return v
    })
}

/**
 * * 修改顶部标题
 * @param title
 */
export const setTitle = (title?: string) => {
    title && (document.title = title)
}

export const cloneDeep = (obj: T, hash = new WeakMap<object, any>()): T => {
    // 处理 null、undefined、原始类型和函数
    if (obj === null || typeof obj !== 'object') {
        return obj as T;
    }

    // 处理 Date 类型
    if (obj instanceof Date) {
        return new Date(obj.getTime()) as T; // 使用 .getTime() 确保类型安全
    }

    // 处理 Array 类型
    if (Array.isArray(obj)) {
        // 检查是否已经克隆过这个数组，防止循环引用
        if (hash.has(obj)) {
            return hash.get(obj) as T;
        }
        const clone = [] as any[]; // 使用 any[] 作为中间类型，稍后在类型断言中转换为 T
        hash.set(obj, clone);
        for (let i = 0; i < obj.length; i++) {
            clone[i] = cloneDeep(obj[i], hash);
        }
        // 类型断言为 T，这里假设 T 是数组类型或可以兼容为数组类型
        return clone as unknown as T; // 使用 unknown 作为中间类型来绕过 TypeScript 的类型检查限制
    }

    // 处理普通对象和其他对象类型（如 Map, Set, RegExp 等）
    // 注意：这个实现没有特殊处理 Map 和 Set，如果需要，请扩展此函数
    if (hash.has(obj)) {
        return hash.get(obj) as T;
    }
    const clone = Object.create(Object.getPrototypeOf(obj)) as any; // 使用 any 作为中间类型
    hash.set(obj, clone);
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            // 这里不需要类型断言，因为我们已经知道 obj[key] 的类型是 any
            clone[key] = cloneDeep(obj[key], hash);
        }
    }
    // 类型断言为 T，这里我们假设 T 是一个对象类型或可以兼容为对象类型
    return clone as T;
}
